package learner.hmm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import learner.Pattern;

public class MovementStats {

	private List<Movement> movements;

	// index of the movement that is used, when methods specific
	// for one movement are called, for example : getMovementVectorForTime()
	private int currentMovement;

	public MovementStats(List<Pattern> patterns) {
		movements = new ArrayList<Movement>();
		for (Pattern pattern : patterns) {
			movements.add(new Movement(pattern));
		}
	}

	public void changeCurrentMovement() {
		currentMovement = (currentMovement + 1) % movements.size();
	}

	public double getRelativeFrequencyOf(MovementVector movementVector) {
		double movementFrequency = 0.0;
		int totalSize = 0;
		for (Movement movement : movements) {
			totalSize += movement.getSize();
			movementFrequency += movement.getSize()
					* movement.getRelativeFrequencyOf(movementVector);
		}
		return movementFrequency / totalSize;
	}

	public Set<MovementVector> getDifferentMovementVectors() {
		Set<MovementVector> movementVectors = new HashSet<MovementVector>();
		for (Movement movement : movements) {
			movementVectors.addAll(movement.getDifferentMovements());
		}
		return movementVectors;
	}

	public Map<MovementVector, Double> getRelativeFreqMovements() {
		Set<MovementVector> movementVectors = getDifferentMovementVectors();
		Map<MovementVector, Double> relativeFrequenciesMovements = new HashMap<MovementVector, Double>();
		for (MovementVector movementVector : movementVectors) {
			relativeFrequenciesMovements.put(movementVector,
					getRelativeFrequencyOf(movementVector));
		}
		return relativeFrequenciesMovements;
	}

	/**
	 * 
	 * @param interval
	 *            , t
	 * @param intervalSize
	 * @return
	 */
	public List<Map<MovementVector, Double>> getRelativeFreqMovementIntervals(
			int nbOfIntervals) {
		List<Map<MovementVector, Double>> intervals = new ArrayList<Map<MovementVector, Double>>();
		int i = 0;
		for (Movement movement : movements) {
			List<Map<MovementVector, Double>> intervalsForMov = movement
					.getRelativeFreqMovementIntervals(nbOfIntervals);
			addNotOccuringMovementVectors(intervalsForMov);
			if (intervals.isEmpty()) {
				intervals = intervalsForMov;
			} else {
				intervals = weightedAverageOfIntervals(intervalsForMov, 1,
						intervals, i);
				i++;
			}
		}
		return intervals;
	}

	private void addNotOccuringMovementVectors(
			List<Map<MovementVector, Double>> intervals) {
		for (MovementVector movVector : getDifferentMovementVectors()) {
			for (Map<MovementVector, Double> conditionalProbs : intervals) {
				if (!conditionalProbs.containsKey(movVector)) {
					conditionalProbs.put(movVector, 0.0);
				}
			}

		}
	}

	private List<Map<MovementVector, Double>> weightedAverageOfIntervals(
			List<Map<MovementVector, Double>> intervals1, int weight1,
			List<Map<MovementVector, Double>> intervals2, int weight2) {
		List<Map<MovementVector, Double>> average = new ArrayList<Map<MovementVector, Double>>();
		for (int i = 0; i < intervals1.size(); i++) {
			average.add(weightedAverageOf(intervals1.get(i), weight1,
					intervals2.get(i), weight2));
		}
		return average;
	}

	public Map<MovementVector, Double> weightedAverageOf(
			Map<MovementVector, Double> movementProbs1, int weight1,
			Map<MovementVector, Double> movementProbs2, int weight2) {
		if (movementProbs1.size() != movementProbs2.size())
			throw new IllegalArgumentException();
		Map<MovementVector, Double> average = new HashMap<MovementVector, Double>();
		for (MovementVector movementVector : movementProbs1.keySet()) {
			double value1 = movementProbs1.get(movementVector);
			double value2 = movementProbs2.get(movementVector);
			double averageValue = (double) (value1 * weight1 + value2 * weight2)
					/ (double) (weight1 + weight2);
			average.put(movementVector, averageValue);
		}
		return average;
	}

	public Movement getMovement(int i) {
		return movements.get(i);
	}
}
